﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Windows.Data;

namespace Xceed.Silverlight.Controls
{
  public class UniformGrid : Grid
  {
    public UniformGrid()
    {
      // Bind to Children.Count since it's the only
      // way to get Count PropertyChanged because
      // Children does not implement INotifyCollectionChanged...
      Binding binding = new Binding();
      binding.RelativeSource = new RelativeSource( RelativeSourceMode.Self );
      binding.Path = new PropertyPath( "Children.Count" );
      binding.Mode = BindingMode.OneWay;

      this.SetBinding( ChildrenCountInternalProperty, binding );

      this.LayoutUpdated += new EventHandler( this.OnLayoutUpdated );
    }

    #region ChildrenCountInternal Property

    internal static readonly DependencyProperty ChildrenCountInternalProperty = DependencyProperty.Register(
      "ChildrenCountInternal",
      typeof( int ),
      typeof( UniformGrid ),
      new PropertyMetadata(
        0,
        new PropertyChangedCallback( UniformGrid.OnLayoutDependentPropertyChanged ) ) );

    #endregion

    #region Columns Property

    public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register(
      "Columns",
      typeof( int ),
      typeof( UniformGrid ),
      new PropertyMetadata(
        1,
        new PropertyChangedCallback( UniformGrid.OnLayoutDependentPropertyChanged ) ) );

    public int Columns
    {
      get
      {
        return ( int )this.GetValue( UniformGrid.ColumnsProperty );
      }
      set
      {
        this.SetValue( UniformGrid.ColumnsProperty, value );
      }
    }

    #endregion

    #region Rows Property

    public static readonly DependencyProperty RowsProperty = DependencyProperty.Register(
      "Rows",
      typeof( int ),
      typeof( UniformGrid ),
      new PropertyMetadata(
        1,
        new PropertyChangedCallback( UniformGrid.OnLayoutDependentPropertyChanged ) ) );

    public int Rows
    {
      get
      {
        return ( int )this.GetValue( UniformGrid.RowsProperty );
      }
      set
      {
        this.SetValue( UniformGrid.RowsProperty, value );
      }
    }


    #endregion

    #region Orientation Property

    public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
      "Orientation",
      typeof( Orientation ),
      typeof( UniformGrid ),
      new PropertyMetadata(
        Orientation.Vertical,
        new PropertyChangedCallback( UniformGrid.OnLayoutDependentPropertyChanged ) ) );

    public Orientation Orientation
    {
      get
      {
        return ( Orientation )this.GetValue( UniformGrid.OrientationProperty );
      }
      set
      {
        this.SetValue( UniformGrid.OrientationProperty, value );
      }
    }

    #endregion

    #region Private Methods

    private static void OnLayoutDependentPropertyChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
      UniformGrid grid = sender as UniformGrid;

      if( grid == null )
        return;

      grid.RedefineGrid( true );
    }

    private void RedefineGrid( bool invalidateMeasure )
    {
      this.UpdateColumnDefinitions();
      this.UpdateRowDefinitions();
      this.UpdateChildren();

      if( invalidateMeasure )
      {
        this.InvalidateMeasure();
      }
    }

    private void UpdateColumnDefinitions()
    {
      int desiredColumnCount = this.Columns;

      if( this.Orientation == Orientation.Vertical )
      {
        desiredColumnCount = ( int )Math.Ceiling( Convert.ToDouble( this.Children.Count ) / Convert.ToDouble( this.Rows ) );
      }

      int currentColumnCount = this.ColumnDefinitions.Count;

      if( desiredColumnCount < currentColumnCount )
      {
        while( desiredColumnCount < currentColumnCount )
        {
          this.ColumnDefinitions.RemoveAt( currentColumnCount - 1 );
          currentColumnCount--;
        }
      }
      else if( desiredColumnCount > currentColumnCount )
      {
        while( desiredColumnCount > currentColumnCount )
        {
          this.ColumnDefinitions.Add( new ColumnDefinition() );
          currentColumnCount++;
        }
      }
    }

    private void UpdateRowDefinitions()
    {
      int desiredRowCount = this.Rows;

      if( this.Orientation == Orientation.Horizontal )
      {
        desiredRowCount = ( int )Math.Ceiling( Convert.ToDouble( this.Children.Count ) / Convert.ToDouble( this.Columns ) );
      }

      int currentRowCount = this.RowDefinitions.Count;

      if( desiredRowCount < currentRowCount )
      {
        while( desiredRowCount < currentRowCount )
        {
          this.RowDefinitions.RemoveAt( currentRowCount - 1 );
          currentRowCount--;
        }
      }
      else if( desiredRowCount > currentRowCount )
      {
        while( desiredRowCount > currentRowCount )
        {
          this.RowDefinitions.Add( new RowDefinition() );
          currentRowCount++;
        }
      }
    }

    private void UpdateChildren()
    {
      int rowMax = this.RowDefinitions.Count;
      int columnMax = this.ColumnDefinitions.Count;

      UIElementCollection children = this.Children;
      int totalChildren = children.Count;
      int childrenIndex = 0;

      if( this.Orientation == Orientation.Horizontal )
      {
        for( int rowIndex = 0; rowIndex < rowMax; rowIndex++ )
        {
          for( int columnIndex = 0; columnIndex < columnMax; columnIndex++ )
          {
            if( childrenIndex >= totalChildren )
              return;

            UIElement child = children[ childrenIndex ];
            child.SetValue( Grid.RowProperty, rowIndex );
            child.SetValue( Grid.ColumnProperty, columnIndex );
            childrenIndex++;
          }
        }
      }
      else
      {
        for( int columnIndex = 0; columnIndex < columnMax; columnIndex++ )
        {
          for( int rowIndex = 0; rowIndex < rowMax; rowIndex++ )
          {
            if( childrenIndex >= totalChildren )
              return;

            UIElement child = children[ childrenIndex ];
            child.SetValue( Grid.RowProperty, rowIndex );
            child.SetValue( Grid.ColumnProperty, columnIndex );
            childrenIndex++;
          }
        }
      }
    }

    #endregion

    #region Private Event Handlers

    private void OnLayoutUpdated( object sender, EventArgs e )
    {
      int count = this.Children.Count;

      if( m_cachedLayoutChildrenCount != count )
      {
        m_cachedLayoutChildrenCount = count;

        this.RedefineGrid( true );
      }
    }

    #endregion

    #region Private Fields

    private int m_cachedLayoutChildrenCount;

    #endregion
  }
}
